﻿//-----------------------------------------------------------------------
// <copyright file="FolderCacheTrigger.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings
using System;
using System.IO;
using System.Security.Principal;
using FastNET.CleanAct.Client.CleanEngineBusiness;
using FastNET.CleanAct.Client.Win32;
#endregion

namespace FastNET.CleanAct.Client.TaskProcessing.Basic.Triggers
{
    /// <summary>
    /// Trigger used to update the local database with current system paths
    /// </summary>
    public class FolderCacheTrigger : TriggerBase
    {
        #region Trigger Events

        /// <summary>
        /// Called when a user logs onto the system
        /// </summary>
        /// <param name="user">WindowsIdentity of the user the event that occured</param>
        public override void SessionLogon(WindowsIdentity user)
        {
            UpdateUserFolders(user);
            base.SessionLogon(user);
        }

        /// <summary>
        /// Called before the execution of the engine starts
        /// </summary>
        public override void EngineBeforeExecute()
        {
            UpdateAllLoggedOnUsers();
            UpdateSystemFolders();
            UpdateDefaultUserFolders();
            base.EngineBeforeExecute();
        }

        /// <summary>
        /// Called when the application starts
        /// </summary>
        public override void ApplicationStart()
        {
            UpdateAllLoggedOnUsers();
            UpdateSystemFolders();
            UpdateDefaultUserFolders();
            base.ApplicationStart();
        }

        #endregion

        #region Private Static Save Path Methods

        /// <summary>
        /// Updates the local database with a system level folder
        /// </summary>
        /// <param name="folderType">KnownCommonFolderType with the folder type to update</param>
        private static void SaveCommonFolderEntry(KnownCommonFolderType folderType)
        {
            string path = SystemFolders.RetrieveCommonFolderPath(folderType, false);

            if (String.IsNullOrEmpty(path))
            {
                path = SystemFolders.RetrieveCommonFolderPath(folderType, true);
            }

            // Make sure we have the full path without variables
            path = Environment.ExpandEnvironmentVariables(path);

            if (!VerifyPath(path))
            {
                DeleteCommonFolderEntry(folderType);
                return;
            }

            DynamicSetting setting = DynamicSetting.RetrieveByProfileAndKey(null, folderType.ToString()) ??
                                     new DynamicSetting { KeyName = folderType.ToString() };

            setting.Value = path;

            setting.Save();
        }

        /// <summary>
        /// Updates the local database with a user specific folder
        /// </summary>
        /// <param name="folderType">KnownUserFolderType with the folder type to update</param>
        /// <param name="user">WindowsIdentity of the user to update</param>
        private static void SaveUserFolderEntry(KnownUserFolderType folderType, WindowsIdentity user)
        {
            string path = SystemFolders.RetrieveUserFolderPath(user, folderType, false);

            if (String.IsNullOrEmpty(path))
            {
                path = SystemFolders.RetrieveUserFolderPath(user, folderType, true);
            }

            // Make sure we have the full path without variables
            path = Environment.ExpandEnvironmentVariables(path);

            if (!VerifyPath(path))
            {
                DeleteUserFolderEntry(folderType, user);
                return;
            }

            UserProfile profile = UserProfile.RetrieveByAccountName(user.User.Value);

            if (profile == null)
            {
                profile = new UserProfile { AccountName = user.User.Value };
                profile.Save();
            }

            DynamicSetting setting = DynamicSetting.RetrieveByProfileAndKey(profile.ID, folderType.ToString()) ??
                                     new DynamicSetting { KeyName = folderType.ToString() };

            setting.Value = path;

            setting.Save();
            profile.MarkUpdated();
            profile.Save();
        }

        /// <summary>
        /// Updates the local database with the default user specific folder
        /// </summary>
        /// <param name="folderType">KnownUserFolderType with the folder type to update</param>
        private static void SaveDefaultUserFolderEntry(KnownUserFolderType folderType)
        {
            string path = SystemFolders.RetrieveDefaultUserFolderPath(folderType, false);

            if (String.IsNullOrEmpty(path))
            {
                path = SystemFolders.RetrieveDefaultUserFolderPath(folderType, true);
            }

            // Make sure we have the full path without variables
            path = Environment.ExpandEnvironmentVariables(path);

            if (!VerifyPath(path))
            {
                DeleteDefaultUserFolderEntry(folderType);
                return;
            }

            UserProfile profile = UserProfile.RetrieveByAccountName("<DEFAULT>");

            if (profile == null)
            {
                profile = new UserProfile { AccountName = "<DEFAULT>" };
                profile.Save();
            }

            DynamicSetting setting = DynamicSetting.RetrieveByProfileAndKey(profile.ID, folderType.ToString()) ??
                                     new DynamicSetting { KeyName = folderType.ToString() };

            setting.Value = path;

            setting.Save();
            profile.MarkUpdated();
            profile.Save();
        }

        #endregion

        #region Private Static Delete Path Methods

        /// <summary>
        /// Deletes a specific common folder entry from the local database
        /// </summary>
        /// <param name="folderType">KnownCommonFolderType with the folder type to remove</param>
        private static void DeleteCommonFolderEntry(KnownCommonFolderType folderType)
        {
            DynamicSetting setting = DynamicSetting.RetrieveByProfileAndKey(null, folderType.ToString());

            if (setting != null)
            {
                setting.Delete();
            }
        }

        /// <summary>
        /// Deletes a specific user folder entry from the local database
        /// </summary>
        /// <param name="folderType">KnownUserFolderType with the folder type to remove</param>
        /// <param name="user">WindowsIdentity of the user to update</param>
        private static void DeleteUserFolderEntry(KnownUserFolderType folderType, WindowsIdentity user)
        {
            UserProfile profile = UserProfile.RetrieveByAccountName(user.User.Value);

            if (profile == null)
            {
                return;
            }

            DynamicSetting setting = DynamicSetting.RetrieveByProfileAndKey(profile.ID, folderType.ToString());

            if (setting != null)
            {
                setting.Delete();
            }
        }

        /// <summary>
        /// Deletes the default user folder entry from the local database
        /// </summary>
        /// <param name="folderType">KnownUserFolderType with the folder type to remove</param>
        private static void DeleteDefaultUserFolderEntry(KnownUserFolderType folderType)
        {
            UserProfile profile = UserProfile.RetrieveByAccountName("<DEFAULT>");

            if (profile == null)
            {
                return;
            }

            DynamicSetting setting = DynamicSetting.RetrieveByProfileAndKey(profile.ID, folderType.ToString());

            if (setting != null)
            {
                setting.Delete();
            }
        }

        #endregion

        #region Private Static Verification Methods

        /// <summary>
        /// Verifies that the retrieved path is valid and accessible before storing the setting in the local database
        /// </summary>
        /// <param name="path">String with the path to verify</param>
        /// <returns></returns>
        private static bool VerifyPath(string path)
        {
            // Make sure we have the full path without variables
            string localPath = Environment.ExpandEnvironmentVariables(path);

            // Is the value empty?
            if (String.IsNullOrEmpty(localPath))
            {
                return false;
            }

            // Are there any invalid characters?
            if (localPath.IndexOfAny(Path.GetInvalidPathChars()) > -1 ||
                localPath.IndexOfAny(Path.GetInvalidPathChars()) > -1)
            {
                return false;
            }

            //Verify the path root is an absolute. For safety reasons, CleanAct does not support any
            //relative system folder paths. They cannot be trusted (Bad memories of WindX 1.0)
            string pathRoot = Path.GetPathRoot(localPath);

            if (String.IsNullOrEmpty(pathRoot) ||
                pathRoot[0] == Path.AltDirectorySeparatorChar ||
                pathRoot[0] == Path.DirectorySeparatorChar)
            {
                return false;
            }

            // Does the path exist?
            return Directory.Exists(localPath);
        }

        #endregion

        #region Private Static Update Calls

        /// <summary>
        /// Updates all system level folders in the local database
        /// </summary>
        private static void UpdateSystemFolders()
        {
            foreach (KnownCommonFolderType folderType in Enum.GetValues(typeof(KnownCommonFolderType)))
            {
                SaveCommonFolderEntry(folderType);
            }
        }

        /// <summary>
        /// Updates all logged on user level folders in the local database
        /// </summary>
        private static void UpdateAllLoggedOnUsers()
        {
            foreach (WindowsIdentity user in WindowsTerminalServer.GetLoggedOnSessions())
            {
                UpdateUserFolders(user);
            }
        }

        /// <summary>
        /// Updates all specific user level folders in the local database
        /// </summary>
        /// <param name="user">WindowsIdentity with the user to process</param>
        private static void UpdateUserFolders(WindowsIdentity user)
        {
            foreach (KnownUserFolderType folderType in Enum.GetValues(typeof(KnownUserFolderType)))
            {
                SaveUserFolderEntry(folderType, user);
            }
        }

        /// <summary>
        /// Updates all default user level folders in the local database
        /// </summary>
        private static void UpdateDefaultUserFolders()
        {
            foreach (KnownUserFolderType folderType in Enum.GetValues(typeof(KnownUserFolderType)))
            {
                SaveDefaultUserFolderEntry(folderType);
            }
        }

        #endregion
    }
}
